<?php

namespace backend\controllers;

use app\models\TaskItem;
use common\core\ZController;
use yii\helpers\Html;
use yii\helpers\Inflector;
use yii\helpers\VarDumper;
use yii\web\ConflictHttpException;
use yii\web\Controller;
use yii\web\Response;

class RbacController extends ZController
{

    /**
     * Get list of application routes
     * @return array
     */
    public function actionIndex()
    {

        $task_id = \Yii::$app->request->get('tid');
        if(!$task_id){
            throw new ConflictHttpException('参数错误');
        }
        $routes = $exists = [];
        //根据任务id获取他的授权项
        $taskModel = new TaskItem();
        $taskModel->task_id = $task_id;
        $exists = $taskModel->getItemByTaskIdForKey();

        //循环把所有controller的action找出来，排除已经赋值的授权项
        foreach($this->getAppRoutes() as $route){
            if(!in_array($route,$exists)){
                $routes[$route] = $route;
            }
        }

        return $this->render('index',[
            'actions' => $routes,
            'exists' => $exists,
            'task_id' => $task_id,
        ]);
    }

    /**
     * 获取所有app下的controller的action,放入缓存
     * @return array|mixed
     */
    public function getAppRoutes($useCache = true)
    {
        $key = 'rbac_all_route';
        $cache = \Yii::$app->cache;

        if ($cache === null || (($result = $cache->get($key)) === false || !$useCache)) {
            $result = [];
            $this->getRouteRecrusive(\Yii::$app, $result);
            if ($cache !== null) {
                $cache->set($key, $result, 2592000);
            }
        }
        return $result;
    }

    /**
     * Get route(s) recrusive
     * @param \yii\base\Module $module
     * @param array $result
     */
    private function getRouteRecrusive($module, &$result)
    {
        $token = "Get Route of '" . get_class($module) . "' with id '" . $module->uniqueId . "'";
        \Yii::beginProfile($token, __METHOD__);
        try {
//            暂时Module下面只有gii和debug,因此不扫描了
//            foreach ($module->getModules() as $id => $child) {
//                if (($child = $module->getModule($id)) !== null) {
//                    $this->getRouteRecrusive($child, $result);
//                }
//            }
            foreach ($module->controllerMap as $id => $type) {
                $this->getControllerActions($type, $id, $module, $result);
            }

            $namespace = trim($module->controllerNamespace, '\\') . '\\';

            $this->getControllerFiles($module, $namespace, '', $result);

//            $result[] = ($module->uniqueId === '' ? '' : '/' . $module->uniqueId) . '/*';

        } catch (\Exception $exc) {
            \Yii::error($exc->getMessage(), __METHOD__);
        }
        \Yii::endProfile($token, __METHOD__);
    }


    /**
     * Get list controller under module
     * @param \yii\base\Module $module
     * @param string $namespace
     * @param string $prefix
     * @param mixed $result
     * @return mixed
     */
    private function getControllerFiles($module, $namespace, $prefix, &$result)
    {
        $path = @\Yii::getAlias('@' . str_replace('\\', '/', $namespace));
        $token = "Get controllers from '$path'";
        \Yii::beginProfile($token, __METHOD__);
        try {
            if (!is_dir($path)) {
                return;
            }
            foreach (scandir($path) as $file) {
                if ($file == '.' || $file == '..') {
                    continue;
                }
                if (is_dir($path . '/' . $file)) {
                    $this->getControllerFiles($module, $namespace . $file . '\\', $prefix . $file . '/', $result);
                } elseif (strcmp(substr($file, -14), 'Controller.php') === 0) {
                    $id = Inflector::camel2id(substr(basename($file), 0, -14));
                    $className = $namespace . Inflector::id2camel($id) . 'Controller';
                    if (strpos($className, '-') === false && class_exists($className) && is_subclass_of($className, 'yii\base\Controller')) {
                        $this->getControllerActions($className, $prefix . $id, $module, $result);
                    }
                }
            }
        } catch (\Exception $exc) {
            \Yii::error($exc->getMessage(), __METHOD__);
        }
        \Yii::endProfile($token, __METHOD__);
    }


    /**
     * Get list action of controller
     * @param mixed $type
     * @param string $id
     * @param \yii\base\Module $module
     * @param string $result
     */
    private function getControllerActions($type, $id, $module, &$result)
    {
        $token = "Create controller with cofig=" . VarDumper::dumpAsString($type) . " and id='$id'";
        \Yii::beginProfile($token, __METHOD__);
        try {
            /* @var $controller \yii\base\Controller */
            $controller = \Yii::createObject($type, [$id, $module]);

            $this->getActionRoutes($controller, $result);
//            $result[] = '/' . $controller->uniqueId . '/*';

        } catch (\Exception $exc) {
            \Yii::error($exc->getMessage(), __METHOD__);
        }
        \Yii::endProfile($token, __METHOD__);
    }

    /**
     * Get route of action
     * @param \yii\base\Controller $controller
     * @param array $result all controller action.
     */
    private function getActionRoutes($controller, &$result)
    {
        $token = "Get actions of controller '" . $controller->uniqueId . "'";
        \Yii::beginProfile($token, __METHOD__);
        try {
            $prefix = '/' . $controller->uniqueId . '/';
            foreach ($controller->actions() as $id => $value) {
                $result[] = $prefix . $id;
            }
            $class = new \ReflectionClass($controller);
            foreach ($class->getMethods() as $method) {
                $name = $method->getName();
                if ($method->isPublic() && !$method->isStatic() && strpos($name, 'action') === 0 && $name !== 'actions') {
                    $result[] = $prefix . Inflector::camel2id(substr($name, 6));
                }
            }
        } catch (\Exception $exc) {
            \Yii::error($exc->getMessage(), __METHOD__);
        }
        \Yii::endProfile($token, __METHOD__);
    }


    /**
     * Assign or remove items
     * @param string $action
     * @return array
     */
    public function actionAssign($action)
    {
        $post = \Yii::$app->getRequest()->post();
        $tid = $post['task_id'];

        if(!$tid){
            echo ['error' => 1,'msg' => '参数错误'];
            \Yii::$app->end();
        }

        $routes = $post['routes'];
        $error = [];
        if ($action == 'assign') {
            $this->saveTaskItem($tid,$routes);
        } else if($action == 'delete') {
            foreach ($routes as $route) {
                $taskModel = TaskItem::findOne(['auth_item' => $route]);
                if(!$taskModel){
                    echo ['error' => 2,'msg' => '请求的权限不存在：'.$route];
                    \Yii::$app->end();
                }

                try {

                    $taskModel->delete();

                } catch (Exception $exc) {
                    $error[] = $exc->getMessage();
                }
            }
        }else{
            echo ['error' => 2,'msg' => '请求类型不存在'];
            \Yii::$app->end();
        }
        \Yii::$app->getResponse()->format = Response::FORMAT_JSON;
        return [$this->actionRouteSearch('new', $tid),
            $this->actionRouteSearch('exists', $tid)];
    }

    /**
     * Search Route
     * @param string $target
     * @param string $term
     * @param string $refresh
     * @return array
     */
    public function actionRouteSearch($target, $task_id)
    {

        $result = [];

        $existsOptions = [];
        $taskItemModel = new TaskItem();
        $taskItemModel->task_id = $task_id;
        $exists = $taskItemModel->getItemByTaskIdForKey();
        $routes = $this->getAppRoutes();
        if ($target == 'new') {
            foreach ($routes as $route) {
                if (in_array($route, $exists)) {
                    continue;
                }
                if (empty($term) or strpos($route, $term) !== false) {
                    $result[$route] = $route;
                }
            }
        } else {

            foreach ($exists as $name) {
                if ($name[0] !== '/') {
                    continue;
                }

                // extract route part from $name
                $r = explode('&', $name);
                if (empty($r[0]) || !in_array($r[0], $routes)) {
                    $existsOptions[$name] = ['class' => 'lost'];
                }
            }
            $result = $exists;
        }
        $options = $target == 'new' ? [] : ['options' => $existsOptions];

        return Html::renderSelectOptions('', $result, $options);
    }


    /**
     * 保存任务和授权项的关系
     * @param $task_id 任务id
     * @param $routes 授权项数组
     * @return bool
     */
    private function saveTaskItem($task_id,$routes)
    {

        $taskItemModel = new TaskItem();
        foreach ($routes as $route) {
            try {
                $new_route = '/' . trim($route, '/');
                $result = $taskItemModel->findOne(['task_id' => $task_id,'auth_item' => $new_route]);
                //如果不存在，就是新数据，创建之
                if(!$result){
                    $taskItemModel->isNewRecord = true;
                    $taskItemModel->id = null;
                    $taskItemModel->task_id = $task_id;
                    $taskItemModel->auth_item = '/' . trim($new_route, '/');
                    if(!$taskItemModel->save()){
                        \Yii::error(VarDumper::dumpAsString($taskItemModel->getErrors()));
                        return false;
                    }
                }
            } catch (Exception $e) {
                var_dump($e);die;
            }
        }
        return true;
    }

}
